
#include "session.h"

#ifndef CONFIG_MEDIA_EMULATE_ON_PC
#include <asm/shdev.h>
#endif
#include <asm/delay.h>

#include <uapi/stdlib.h>
#include <uapi/string.h>

#ifdef CONFIG_MEDIA_EMULATE_ON_PC
#include "ff/ff.h"
#else
#include "ff.h"

#if !defined(CONFIG_HAVE_DATBUFS) || defined(CONFIG_NO_DATBUFS)
#error configuration CONFIG_HAVE_DATBUFS must be defined, configuration CONFIG_NO_DATBUFS must be deleted !
#endif
#endif
///////////////////////////////////////////////////////////////////////////////////////////////
// XXX
///////////////////////////////////////////////////////////////////////////////////////////////

typedef struct
{
	struct SESSION *session, sessiondat;

	long load_size;
	long load_offset;
	psysbuf_t load_buf;

	FIL file;

}session_unit_t;

static long SESSIONAPI(StreamLoad)(session_unit_t *punit)
{
	uint8_t *ldat;
	long l2, off, ret = 0;
	psysbuf_t load_buf;

	load_buf = punit->load_buf;
	if (load_buf == NULL) {
		load_buf = sysbuf_alloc(SYSBUF_GROUP_DATBUFS);
		if (load_buf == NULL)
			return -1;
		punit->load_buf = load_buf;
	}

	if (punit->load_size > 0)
	{
		l2 = (load_buf->maxsize / TS_PKTLEN) * TS_PKTLEN;
		if (l2 >= punit->load_size) {
			l2 = punit->load_size;
			load_buf->flags |= SYS_BUF_FLAG_EOF;
		}

#ifdef CONFIG_MEDIA_EMULATE_ON_PC
		f_read(&(punit->file), (void*)(load_buf->haddr), l2, NULL);

#else
		f_read(&(punit->file), (void*)HWADDR(load_buf->haddr), l2, NULL);
#endif
		load_buf->size = l2;
		punit->load_offset += l2;
		punit->load_size -= l2;
	}
	else
	{
		psysbuf_t buf = SessionBufferPop(punit->session);
		if (buf) {
			if (buf->list.group == load_buf->list.group) {
				load_buf = buf;
			}
			else {
				phys_addr_t src, dst;
				src = buf->haddr + buf->offset;
				dst = load_buf->haddr + load_buf->offset;
#ifdef CONFIG_MEDIA_EMULATE_ON_PC
				memcpy((void*)dst, (void*)src, buf->size);
#else
				edma_copy(EDMA_CHN_IO, dst, src, buf->size);
#endif
				load_buf->size = buf->size;
				if (buf->flags & SYS_BUF_FLAG_EOF)
					load_buf->flags |= SYS_BUF_FLAG_EOF;
				load_buf->user = buf->user;
				sysbuf_free(buf);
			}
		}
		else {
			load_buf = NULL;
		}
	}

	if (load_buf) {
		session_buffer_t *sbf = SessionBufferPush(&punit->sessiondat, load_buf);
		if (sbf) {
			if (punit->load_buf == load_buf)
				punit->load_buf = NULL;
			ret = load_buf->size;
		}
		else {
			if (punit->load_buf != load_buf)
				sysbuf_free(load_buf);
			ret = 0;
		}
	}

	return ret;
}

static
SESSIONSTATE SESSIONAPI(SessionRun)(struct SESSION *session)
{
	long res;
	psysbuf_t buf;

	session_unit_t *punit = NULL;
	if (session != NULL)
		punit = (session_unit_t*)session->handle;
	if (punit == NULL)
		return SSTATE_NULL;

	if (session->state != SSTATE_RUNNING
		&& session->state != SSTATE_RUNNING_IDLE)
		return session->state;

	session->state = SSTATE_RUNNING_IDLE;

	/* try to fill stream buffer */
	res = SESSIONAPI(StreamLoad)(punit);

	/* nothing done then, get and free */
	buf = SessionBufferPop(&punit->sessiondat);
	if (buf) {
		if (buf->flags & SYS_BUF_FLAG_EOF) {
			// nothing done then
		}

		sysbuf_free(buf);
	}

	return session->state;
}

static
int SESSIONAPI(SSCMD_STREAM_START)(struct SESSION *session, session_file_t *file)
{
	session_unit_t *punit = NULL;
	if (session != NULL)
		punit = (session_unit_t*)session->handle;
	if (punit == NULL || session->state < SSTATE_INITED)
		return -EPERM;

	/* Initial loading */
	punit->load_buf = NULL;
	punit->load_size = 0;
	punit->load_offset = 0;

	if (file->filename) {
		if (f_open(&(punit->file), file->filename, FA_READ) != FR_OK)
		{
			debug("open stream file error \n");
			return -EBADF;
		}
		else
		{
			punit->load_size = f_size(&(punit->file));
		}
	}

	SessionBufferDeInit(session);
	SessionBufferDeInit(&punit->sessiondat);

	session->state = SSTATE_RUNNING;

	return 0;
}

static
int SESSIONAPI(SSCMD_STREAM_STOP)(struct SESSION *session)
{
	SESSIONSTATE state;
	session_unit_t *punit = NULL;
	if (session != NULL)
		punit = (session_unit_t*)session->handle;
	if (punit == NULL || session->state < SSTATE_RUNNING)
		return -EPERM;

	// set state
	session->state = SSTATE_STOP;

	if (punit->load_buf) {
		sysbuf_free(punit->load_buf);
		punit->load_buf = NULL;
	}

	f_close(&punit->file);

	SessionBufferDeInit(session);
	SessionBufferDeInit(&punit->sessiondat);

	// set state
	session->state = SSTATE_INITED;

	return 0;
}

static
int SESSIONAPI(SessionCommand)(struct SESSION *session, int cmd, void *params)
{
	int ret = 0;
	session_unit_t *punit = NULL;
	if (session != NULL)
		punit = (session_unit_t*)session->handle;
	if (punit == NULL)
		return -EINVAL;

	switch (cmd)
	{
	case SSCMD_STREAM_START:
	{
		debug("Session '%s' command 'SSCMD_STREAM_START'\n", session->name);
		return SESSIONAPI(SSCMD_STREAM_START)(session, (session_file_t *)params);
	}break;
	case SSCMD_STREAM_PAUSE:
	{
		debug("Session '%s' command  'SSCMD_STREAM_PAUSE'\n", session->name);
		if (session->state == SSTATE_RUNNING
			|| session->state == SSTATE_RUNNING_IDLE)
			session->state = SSTATE_RUNNING_PAUSE;
	}break;
	case SSCMD_STREAM_RESUME:
	{
		debug("Session '%s' command  'SSCMD_STREAM_RESUME'\n", session->name);
		if (session->state == SSTATE_RUNNING_PAUSE)
			session->state = SSTATE_RUNNING;
	}break;
	case SSCMD_STREAM_STOP:
	{
		debug("Session '%s' command  'SSCMD_STREAM_STOP'\n", session->name);
		return SESSIONAPI(SSCMD_STREAM_STOP)(session);
	}break;
	case SSCMD_STREAM_BUFCHANGED:
	{
	}break;
	default:
	{
		debug("Session '%s' command %d unknown !\n", session->name, cmd);
		ret = -EINVAL;
	};
	};

	return ret;
}

static
void SESSIONAPI(SessionDeInit)(struct SESSION *session)
{
	session_unit_t *punit = NULL;
	if (session != NULL)
		punit = (session_unit_t*)session->handle;
	else
		return;

	debug("SessionDeInit : %s ..\n", session->name);

	SessionBufferDeInit(session);
	SessionBufferDeInit(&punit->sessiondat);

	if (punit != NULL) {
		free(punit);
	}

	session->handle = NULL;
}

static
SESSIONHANDLE SESSIONAPI(SessionInit)(struct SESSION *session)
{
	session_unit_t *punit = (session_unit_t *)malloc(sizeof(session_unit_t) + 64);
	if (punit == NULL)
		return NULL;

	debug("SessionInit : %s ..\n", session->name);

	memset(punit, 0, sizeof(session_unit_t));
	punit->session = session;
	session->SessionRun = &SESSIONAPI(SessionRun);
	session->SessionCommand = &SESSIONAPI(SessionCommand);
	session->SessionDeInit = &SESSIONAPI(SessionDeInit);
	session->state = SSTATE_INITED;

	SessionBufferInit(session);
	SessionBufferInit(&punit->sessiondat);

	session->handle = (SESSIONHANDLE)punit;
	return session->handle;
}

///////////////////////////////////////////////////////////////////////////////

struct SESSION gSession_xxx =
{ "xxx", (1 << SST_TYPES),
&SESSIONAPI(SessionInit),
};
